package cn.edu.nju.ws.sview.reasoning;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

/**
 * @author Gong Cheng
 * @author Gong Saisai
 */
public class Atom<T extends Term> {
	private Predicate predicate = null;
	protected ArrayList<T> terms = null;
    private int hash;
	/**
	 * construct atom
	 * 
	 * @param predicate
	 *           atom predicate
	 * @param terms
	 *           atom term
	 */
	public Atom(Predicate predicate, ArrayList<T> terms) {
		this.predicate = predicate;
		this.terms = terms;
	}

	/**
	 * 
	 * @return atom predicate
	 */
	public Predicate getPredicate() {
		return this.predicate;
	}

	/**
	 * 
	 * @return  atom term
	 */
	public ArrayList<T> getTerms() {
		return new ArrayList<T>(this.terms);
	}
     
	/**
	 * get argument size
	 * @return
	 */
	public int size(){
		return terms.size();
	}
	
	/**
	 * get specific argument
	 * @param i
	 * @return
	 */
	public T get(int i){
		return terms.get(i) ;
	}
   
	/**
	 *get Atom's variables
	 * @return
	 */
	public Set<Variable> getVariables(){
		Set<Variable> set = new HashSet<Variable>();
		for(Term term:terms){
			if(term instanceof Variable)
				set.add((Variable)term) ;
		}
		return set;
	}
	
	/**
	 *get atom pattern
	 * @return
	 */
    public Pattern getPattern(){
    	ArrayList<Integer> constIndices = new ArrayList<Integer>();
    	ArrayList<Integer> varIndices= new ArrayList<Integer>();
    	ArrayList<Constant> constMap = new ArrayList<Constant>();
    	HashMap<Integer,Term> varMap = new HashMap<Integer,Term>();
    	Pattern pattern = new Pattern();
    	HashMap<Term,ArrayList<Integer>> myMap = new HashMap<Term,ArrayList<Integer>>();
    	for(int i=0;i<this.terms.size();i++){
    		Term term = this.terms.get(i);
    		if(!myMap.containsKey(term)){
    			myMap.put(term, new ArrayList<Integer>()) ;
    		}
    		myMap.get(term).add(i) ;
    		if(term instanceof Constant){
    			constIndices.add(i) ;
    			constMap.add((Constant)term) ;
    		}else if(term instanceof Variable){
    			varIndices.add(i) ;
    			varMap.put(i, term) ;
    		}
    	}
    	ArrayList<ArrayList<Integer>> equalTerms = new ArrayList<ArrayList<Integer>> ();
    	for(Term term:myMap.keySet()){
    		if(myMap.get(term).size()>1)
    		equalTerms.add(myMap.get(term)) ;
    	}
        pattern.setConstIndices(constIndices) ;
        pattern.setConstTerms(constMap) ;
        pattern.setVarIndices(varIndices) ;
        pattern.setVarMap(varMap) ;
        pattern.setEqualTerms(equalTerms) ;
    	return pattern;
    }
    
	public boolean equals(Object atom) {
		if (atom instanceof Atom<?>) {
			Atom<?> atomO = (Atom<?>) atom;
			if (this.predicate.equals(atomO.predicate)
					&& this.terms.equals(atomO.terms)) {
				return true;
			}
		}
		return false;
	}

	public int hashCode() {
		int h =this.hash;
		if(h==0){
			 h  = this.predicate.hashCode() + this.terms.hashCode();
			 this.hash = h;
		}
		return h;
	}

	public String toString() {
		StringBuffer buffer = new StringBuffer();
		buffer.append(this.predicate);
		buffer.append("(");
		for (int i = 0; i < this.terms.size(); i++) {
			buffer.append(this.terms.get(i));
			if (i != this.terms.size() - 1)
				buffer.append(",");
		}
		buffer.append(")");
		return buffer.toString();
	}

}